﻿using Microsoft.Extensions.Caching.Distributed;
using Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace vueweb
{
	public static class CacheHelper
	{
		public static IDistributedCache Cache
		{
			get
			{
				return (IDistributedCache)Singleton<IServiceProvider>.Instance.GetService(typeof(IDistributedCache));
			}
		}

		public static Dictionary<string, byte[]> GetSessionData(this IDistributedCache cache, string sessionKey)
		{
			byte[] data = cache.Get(sessionKey);
			return Deserialize(data);
		}

		private static int DeserializeNumFrom3Bytes(Stream content)
		{
			return content.ReadByte() << 16 | content.ReadByte() << 8 | content.ReadByte();
		}

		private static int DeserializeNumFrom2Bytes(Stream content)
		{
			return content.ReadByte() << 8 | content.ReadByte();
		}

		private static int DeserializeNumFrom4Bytes(Stream content)
		{
			return content.ReadByte() << 24 | content.ReadByte() << 16 | content.ReadByte() << 8 | content.ReadByte();
		}

		private static byte[] ReadBytes(Stream stream, int count)
		{
			var output = new byte[count];
			int total = 0;
			while (total < count)
			{
				var read = stream.Read(output, total, count - total);
				if (read == 0)
				{
					throw new EndOfStreamException();
				}
				total += read;
			}
			return output;
		}

		private const byte SerializationRevision = 2;
		private const int IdByteCount = 16;

		private static Dictionary<string, byte[]> Deserialize(byte[] data)
		{
			if (data == null)
			{
				return new Dictionary<string, byte[]>();
			}

			Stream content = new MemoryStream(data);
			if (content == null || content.ReadByte() != SerializationRevision)
			{
				return new Dictionary<string, byte[]>();
			}

			Dictionary<string, byte[]> _store = new Dictionary<string, byte[]>();
			int expectedEntries = DeserializeNumFrom3Bytes(content);
			ReadBytes(content, IdByteCount);

			for (int i = 0; i < expectedEntries; i++)
			{
				int keyLength = DeserializeNumFrom2Bytes(content);
				var key = new SessionEncodedKey(ReadBytes(content, keyLength));
				int dataLength = DeserializeNumFrom4Bytes(content);
				_store[key.KeyString] = ReadBytes(content, dataLength);
			}

			return _store;
		}
	}
}
